`timescale 1ns/1ps
module	SPI
#  
(   parameter	T	=	100,		//SPI时钟周期，1MHz
	parameter   Len_WR      =   24,		//写指令的位数(写入指令+写入地址+写入的数据)
	parameter   Len_RD      =   16
)
(
	
	//global clock
	input				iClk,		//25M
	input				iRst_n,		//system reset
	
	//spi interface
	output				o_sck,	//spi clock
	output				o_mosi,	//spi master out  slave in
	input               i_miso,   //spi master in  slave out
	output              o_ssn,     //spi cs

	//user interface
	input		[Len_WR-1:0]	ivCmd_write,	   // 命令+地址+数据(写)
	input		[Len_RD-1:0]	ivCmd_read,	   // 命令+地址(读)
    output      reg [7:0]   ovData,           //读出的数据
    input               iWrite_En,    //开始写
	input               iRead_En,    //开始读
	output				oSpi_INT,
	output     [5:0] 	state       

);


/*****************************************
*****************************************/

reg  [1:0] config_wrbegin_r;//写使能边沿处理
reg  [1:0] config_rdbegin_r;//读使能边沿处理
wire  config_wrbegin_pos;//写使能下降沿
wire  config_rdbegin_pos;//读使能下降沿
//边沿信号处理
always@(posedge iClk or negedge iRst_n)
begin
  if(!iRst_n)
   begin
     config_wrbegin_r <= 2'b00;
     config_rdbegin_r <= 2'b00;
   end
  else
   begin
     config_wrbegin_r <= {config_wrbegin_r[0],iWrite_En};
     config_rdbegin_r <= {config_rdbegin_r[0],iRead_En};
   end
end
//assign config_wrbegin_pos = (~config_wrbegin_r[0] & iWrite_En) ? 1'b1 : 1'b0;
//assign config_rdbegin_pos = (~config_rdbegin_r[0] & iRead_En) ? 1'b1 : 1'b0;
assign config_wrbegin_pos = config_wrbegin_r==2'b01;//上升沿
assign config_rdbegin_pos = config_rdbegin_r==2'b01;

//----------------------------------------

assign  state=current_state;
reg	[16:0]	delay_cnt;
localparam	DELAY_TOP = 100;	//延时时间

always@(posedge iClk or negedge iRst_n)
begin
	if(!iRst_n)
		delay_cnt <= 0;
		
	else if(config_wrbegin_pos)
		delay_cnt <= 0;
	else if(config_rdbegin_pos)
		delay_cnt <= 0;	
		
	else if(delay_cnt < DELAY_TOP)
		delay_cnt <= delay_cnt + 1'b1;
	else
		delay_cnt <= delay_cnt;
end
wire	delay_done = (delay_cnt == DELAY_TOP) ? 1'b1 : 1'b0;	//10us delay

//----------------------------------------
//SPI Control Clock generate
reg	[15:0]	rvClk_cnt;	//divide for SPI clock
/******************************************


*******************************************/
reg	rvClk_spi;		//SPI control clock, H: valid; L: valid
reg	rSPItransfer_En;	//send SPI data	before, make sure that sdat is steady when SPI_sclk is valid
reg	rSPI_capture_En;		//capture SPI data	while sdat is steady from cmos 				
always@(posedge iClk or negedge iRst_n)
begin
	if(!iRst_n)
		begin
		rvClk_cnt         <= 0;
		rvClk_spi    <= 1'b0;
		rSPItransfer_En <= 0;
		rSPI_capture_En  <= 0;
		end
	else if(config_wrbegin_pos)
		begin
		rvClk_cnt         <= 0;
		rvClk_spi    <= 1'b0;
		rSPItransfer_En <= 0;
		rSPI_capture_En  <= 0;
		end	
	else if(config_rdbegin_pos)
		begin
		rvClk_cnt         <= 0;
		rvClk_spi    <= 1'b0;
		rSPItransfer_En <= 0;
		rSPI_capture_En  <= 0;
		end		
	else if(delay_done)  //10us
		begin
		if(rvClk_cnt < (T) - 1'b1) 
			rvClk_cnt <= rvClk_cnt + 1'd1;
		else
			rvClk_cnt <= 0;
		//SPI control clock, H: valid; L: valid
/*************************************************************************
*************************************************************************/		
		//时钟生成，T指时钟周期
		rvClk_spi <= ( (rvClk_cnt >=((1*T)/4 + 8'd10) ) && (rvClk_cnt <= (3*T)/4 + 8'd10)) ? 1'b1 : 1'b0;
		//发送数据，下降沿
		rSPItransfer_En <= (rvClk_cnt == (3*T)/4 + 8'd12) ? 1'b1 : 1'b0;
		//接收数据，上升沿
		rSPI_capture_En <= (rvClk_cnt == (1*T)/4  + 8'd11) ? 1'b1 : 1'b0; 
		end
	else
		begin
		rvClk_cnt         <= 0;
		rvClk_spi    <= 1'b0;
		rSPItransfer_En <= 0;
		rSPI_capture_En  <= 0;
		end
end

//-----------------------------------------
//******状态机定义********
//write
localparam	IDLE             = 6'd0;//空闲
localparam	WR_START         = 6'd1;//准备移位，写命令赋值
localparam	WR_SHIFT 		 = 6'd2;//写数据移位MOSI(一共24个，命令+地址+数据)
localparam	WR_END		     = 6'd3;//写操作完成
localparam	WR_STOP		     = 6'd4;//SPI写中断
//read
localparam	RD_START	     = 6'd5;//准备移位，读命令赋值		
localparam	RD_SHIFT	     = 6'd6;//读命令赋值，mosi移位(一共16个，命令+地址)
localparam	RD_GETDATA       = 6'd7;//读数据移位MISO，一共8个
localparam	RD_END		     = 6'd8;//读操作完成
localparam	RD_STOP	         = 6'd9;//SPI读中断
//-----------------------------------------


// FSM: always1
reg	[5:0]	current_state, next_state; //SPI write and read state  
always@(posedge iClk or negedge iRst_n)
begin
	if(!iRst_n)
		current_state <= IDLE;
	else if(config_wrbegin_pos)
		current_state <= IDLE;	
	else if(config_rdbegin_pos)
		current_state <= IDLE;		
	else if(rSPItransfer_En)
		current_state <= next_state;
end

//-----------------------------------------
wire	rSPItransfer_End = ( (current_state == WR_STOP || current_state == RD_STOP)&&rSPItransfer_En ) ? 1'b1 : 1'b0;
assign	oSpi_INT = rSPItransfer_End;
//-------------------------------------------------------------------------------------------------

// FSM: always2
reg	[4:0]	spi_stream_cnt;	//SPI data bit stream count
always@(*)
begin
	next_state = IDLE; 	//state initialization
	case(current_state)
	IDLE:		//5'd0
		begin
		if(delay_done)	//delay_done //1ms Setting time after software/hardware reset	
			begin
			if(rSPItransfer_En)
				begin
					if(config_wrbegin_r[1] == 1'b1)
						next_state = WR_START;	
					else if(config_rdbegin_r[1] == 1'b1)
						next_state = RD_START;	
					else
						next_state = IDLE;	
				end
			else
				next_state = next_state;
			end
		else
				next_state = IDLE;		
		end

	//o_mosi 
	//{ 0x02, REG_Address, W_REG_Data }
	WR_START:	//开始写
		begin
		if(rSPItransfer_En)	next_state = WR_SHIFT;
		else				next_state = WR_START;
		end

	WR_SHIFT:	//6'd6
		if(rSPItransfer_En == 1'b1 && spi_stream_cnt == Len_WR)	
							next_state = WR_END;
		else				next_state = WR_SHIFT;


	WR_END:	//
		if(rSPItransfer_En)	next_state = WR_STOP;
		else				next_state = WR_END;
		
	WR_STOP:	//
		if(rSPItransfer_En)	next_state = IDLE;
		else				next_state = WR_STOP;
		
		
	//SPI Read: { COM , REG_Address, R_REG_Data}
	RD_START:	//
		if(rSPItransfer_En)	next_state = RD_SHIFT;
		else				next_state = RD_START;
	
		
	RD_SHIFT:	//
		if(rSPItransfer_En == 1'b1 && spi_stream_cnt == Len_RD)	
							next_state = RD_GETDATA;
		else				next_state = RD_SHIFT;
		
	RD_GETDATA:	//
			if(rSPItransfer_En == 1'b1 && spi_stream_cnt == Len_RD+8)	//读指令加8个周期的数据读取
								next_state = RD_END;
			else				next_state = RD_GETDATA;

	RD_END:	//
		if(rSPItransfer_En)	next_state = RD_STOP;
		else				next_state = RD_END;
		
	RD_STOP:	//
		if(rSPItransfer_En)	next_state = IDLE;
		else				next_state = RD_STOP;	
		

	default:;	//default vaule		
	endcase
end

//-----------------------------------------
// FSM: always3
reg	        spi_sdat_out;		// mosi数据
reg	[23:0]	spi_wdata_addr;
always@(posedge iClk or negedge iRst_n)
begin
	if(!iRst_n)
		begin
		spi_sdat_out <= 1'b1;
		spi_stream_cnt <= 0;
	    spi_wdata_addr <= 0;
		end
	else if(config_wrbegin_pos)
		begin
		spi_sdat_out <= 1'b1;
		spi_stream_cnt <= 0;
	    spi_wdata_addr <= 0;

		end	
	else if(config_rdbegin_pos)
		begin
		spi_sdat_out <= 1'b1;
		spi_stream_cnt <= 0;
	    spi_wdata_addr <= 0;

		end		
	else if(rSPItransfer_En)
		begin
		case(next_state)
		IDLE:	//0
			begin
			spi_sdat_out <= 1'b1;		//idle state
			spi_stream_cnt <= 0;
		    spi_wdata_addr <= 0;

			end
		//Write SPI: {0x02, REG_Address, W_REG_Data}
		WR_START:	//1
			begin
			spi_sdat_out <= 1'b1;
			spi_stream_cnt <= 0;
			spi_wdata_addr <= ivCmd_write;	//REG_Address  9bit
			end

		WR_SHIFT:	//MOSI生成
			begin
			spi_stream_cnt <= spi_stream_cnt + 1'b1;
			spi_sdat_out <= spi_wdata_addr[Len_WR-1 - spi_stream_cnt]; //0-23 = 24BIT
			end
		
		WR_END:	//
			   spi_stream_cnt <= 0;
		WR_STOP:	//
			  spi_sdat_out <= 1'b1;
			  
			  
		//SPI Read: COM , ID_Address , R_REG_Data
		RD_START:	//
			begin
			spi_sdat_out <= 1'b1;
			spi_stream_cnt <= 0;
			//spi_wdata_addr[Len-1:Len-8] <= 8'd0;  //
			spi_wdata_addr[Len_RD-1:0]  <= ivCmd_read;  // cmd addr
			end

		RD_SHIFT:	//
			begin
			spi_stream_cnt <= spi_stream_cnt + 1'b1;
			spi_sdat_out <= spi_wdata_addr[Len_RD-1 - spi_stream_cnt];	
			end
//		RD_FLAG:	//
//		   begin
//			spi_stream_cnt <= 0;
//			spi_sdat_out <= 1'b0;    //read '0'
//         end

		RD_GETDATA:	          
				spi_stream_cnt <= spi_stream_cnt + 1'b1;
		RD_END:	//
		   begin
				spi_stream_cnt <= 0;
				spi_sdat_out <= 1'b1;	
			end	
		RD_STOP:
			   spi_sdat_out <= 1'b1;
			  
		
      default: ;
		endcase
		end
	else
		begin
		spi_stream_cnt <= spi_stream_cnt;
		spi_sdat_out <= spi_sdat_out;
		end
end
//read data
reg [7:0]rdata;
always@(posedge iClk or negedge iRst_n)
begin
	if(!iRst_n)
		begin
		rdata <= 0;
		ovData <=8'd0;;
		end
//	else if(!iWrite_En)
//		begin
//		rdata <= 0;
//		end	
	else if(config_rdbegin_pos)
		begin
		rdata <= 0;
		end	
	else if(rSPI_capture_En)
		begin
		case(next_state)

		RD_GETDATA:	rdata[7:0] <= {rdata[6:0],i_miso};
		RD_END: ovData <= rdata;
		default: ;
		endcase
		end
	else
		begin
		rdata <= rdata;
		ovData<= ovData;
		end 
end



//---------------------------------------------------
wire	bir_en_clk =(	current_state == IDLE || current_state == WR_START || 
							current_state == WR_END || current_state == WR_STOP||
						   current_state == RD_START || current_state == RD_END||
							current_state == RD_STOP	) ? 1'b1 : 1'b0;
								
assign o_sck = ( ((current_state >= IDLE && current_state <= WR_STOP )||
						  (current_state >= RD_START && current_state <= RD_STOP )  ) && (~bir_en_clk) ) ? 
																				rvClk_spi : 1'b0;
																																							
//assign o_mosi = (~bir_en_clk) ? spi_sdat_out : 1'b1;
assign o_mosi = spi_sdat_out;

/*wire	bir_en_cs =(	current_state == WR_ACK1B || current_state == WR_ACK2B || current_state == WR_ACK3B || current_state == WR_ACK4B || 
					      current_state == RD_ACK1B || current_state == RD_ACK2B || current_state == RD_ACK3B || current_state == RD_ACK4B || current_state == RD_NACKb
																) ? 1'b1 : 1'b0;	
	*/				
assign  o_ssn = ( (current_state >= WR_START && current_state <= WR_END )||
                   (current_state >= RD_START && current_state <= RD_END )  ) ? 1'b0 : 1'b1;




endmodule